package org.blame.lotterysystem.service.impl;

import lombok.AllArgsConstructor;
import org.blame.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.blame.lotterysystem.common.exception.ServiceException;
import org.blame.lotterysystem.common.utils.JacksonUtil;
import org.blame.lotterysystem.common.utils.RedisUtil;
import org.blame.lotterysystem.controller.param.*;
import org.blame.lotterysystem.dao.dataObject.ActivityDO;
import org.blame.lotterysystem.dao.dataObject.ActivityPrizeDO;
import org.blame.lotterysystem.dao.dataObject.ActivityUserDO;
import org.blame.lotterysystem.dao.dataObject.PrizeDO;
import org.blame.lotterysystem.dao.mapper.*;
import org.blame.lotterysystem.service.ActivityService;
import org.blame.lotterysystem.service.dto.*;
import org.blame.lotterysystem.service.enums.ActivityPrizeEnum;
import org.blame.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.blame.lotterysystem.service.enums.ActivityStatusEnum;
import org.blame.lotterysystem.service.enums.ActivityUserEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {

    private static final Logger logger= LoggerFactory.getLogger(ActivityServiceImpl.class);

    private final String ACTIVITY_PREFIX="ACTIVITY_PREFIX";
    private final Long ACTIVITY_TIMEOUT=60*60*24*3L;

    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public ActivityDTO createActivity(CreateActivityParam activityParam) {

//        校验活动信息是否正确
        checkActivity(activityParam);
//        保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(activityParam.getActivityName());
        activityDO.setDescription(activityParam.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
//        保存活动相关的人员信息
        List<ActivityPrizeListParam> userParams = activityParam.getActivityPrizeList();
        List<ActivityUserDO> activityUserList=activityParam.getActivityUserList()
                .stream()
                .map(userParam->{
                    ActivityUserDO activityUserDO=new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setStatus(ActivityUserEnum.INIT.name());
                    return activityUserDO;
        }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserList);
//        保存活动相关的奖品信息
        List<ActivityPrizeListParam> prizeParams=activityParam.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeList=activityParam.getActivityPrizeList()
                .stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO=new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeList);
//        整合完整的活动信息，返回Redis
//  总结好完整的信息，存放在redis中 ->奖品信息，人员，活动

        List<Long> prizeId=activityParam.getActivityPrizeList()
                .stream()
                .map(ActivityPrizeListParam::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeId);
        ActivityDetailDTO activityDetailDTOList=convertActivityDetailDTO(prizeDOList,
                activityPrizeList,activityUserList,activityDO);

//        将信息存储到redis缓存中
        cacheActivity(activityDetailDTOList);
//        构造返回的信息
        ActivityDTO activityDTO=new ActivityDTO();
        activityDTO.setActivityId(activityDO.getId());
        return activityDTO;
    }

    @Override
    public PageListDTO<ActivityListDTO> findActivityList(PageParam param) {
        int total=activityMapper.count();
        List<ActivityDO> activityDOList=activityMapper.selectActivityList(param.offset(),param.getPageSize());

        List<ActivityListDTO> activityListDTOList=activityDOList
                .stream()
                .map(activityDO -> {
                    ActivityListDTO activityListDTO=new ActivityListDTO();
                    activityListDTO.setActivityId(activityDO.getId());
                    activityListDTO.setActivityName(activityDO.getActivityName());
                    activityListDTO.setDescription(activityDO.getDescription());
                    activityListDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityListDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total,activityListDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetailById(Long activityId) {
        if(activityId==null){
            logger.warn("activityId 为空");
            return null;
        }
        ActivityDetailDTO activityDetailDTO =getCacheActivity(activityId);
        if(activityDetailDTO!=null){
            logger.info("查询redis成功，返回数据 :{}",JacksonUtil.writeValueAsString(activityDetailDTO));
            return activityDetailDTO;
        }
        // 如果redis不存在，查表
        ActivityDO aDO=activityMapper.selectById(activityId);

        List<ActivityPrizeDO> apDOList=activityPrizeMapper.selectByActivityId(activityId);

        List<ActivityUserDO> auDOList=activityUserMapper.selectByActivityId(activityId);

        List<Long> prizeIds=apDOList
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());

        List<PrizeDO> pDO=prizeMapper.batchSelectByIds(prizeIds);
        // 整合活动详细信息，存放redis
        activityDetailDTO = convertActivityDetailDTO(pDO, apDOList,auDOList,aDO);
        cacheActivity(activityDetailDTO);
        // 返回
        return activityDetailDTO;

    }

    @Override
    public void cacheActivity(Long activityId) {
        if(activityId==null){
            logger.warn("活动Id为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        ActivityDO activityDO=activityMapper.selectById(activityId);
        if(activityDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        List<ActivityUserDO> auDO = activityUserMapper.selectByActivityId(activityId);
        List<ActivityPrizeDO> apDO = activityPrizeMapper.selectByActivityId(activityId);
        List<Long> prizeIds=apDO
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList=prizeMapper.batchSelectByIds(prizeIds);

        cacheActivity(convertActivityDetailDTO(prizeDOList,apDO,auDO,activityDO));

    }

    private void cacheActivity(ActivityDetailDTO activityDetailDTOList) {
        if(activityDetailDTOList==null || activityDetailDTOList.getActivityId()==null){
            logger.warn("activityDetailDTOList中的信息为空 :{}", JacksonUtil.writeValueAsString(activityDetailDTOList));
        }
        try {

            redisUtil.set(ACTIVITY_PREFIX+activityDetailDTOList.getActivityId()
                    ,JacksonUtil.writeValueAsString(activityDetailDTOList)
                    ,ACTIVITY_TIMEOUT);

        }catch (Exception e){
            logger.error("cacheActivity  redis缓存失败 :{}",JacksonUtil.writeValueAsString(activityDetailDTOList),e);
        }
    }

    private ActivityDetailDTO getCacheActivity(Long activityId){
        if(activityId==null){
            logger.warn("activityId为空");
        }
        try {
            String s = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if(!StringUtils.hasText(s)){
                logger.warn("缓存中的该信息为空");
                return null;
            }
            return  JacksonUtil.readValue(s, ActivityDetailDTO.class);
        }catch (Exception e){
            logger.error("缓存中没有该信息:{}",JacksonUtil.writeValueAsString(activityId));
            return null;
        }
    }


    private ActivityDetailDTO convertActivityDetailDTO(List<PrizeDO> prizeDOList,
                                                       List<ActivityPrizeDO> activityPrizeDOList,
                                                       List<ActivityUserDO> activityUserDOList,
                                                       ActivityDO activityDO) {
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        activityDetailDTO.setDescription(activityDO.getDescription());

        // activityPrizeDO: {prizeId，amount, status}, {prizeId，amount, status}
        // prizeDO: {prizeId, name....},{prizeId, name....},{prizeId, name....}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList=activityPrizeDOList
                .stream()
                .map(activityPrizeDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(activityPrizeDO.getPrizeId()))
                            .findFirst();
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setDescription(prizeDO.getDescription());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setPrice(prizeDO.getPrice());

                    });
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setStatus(ActivityPrizeEnum.forName(activityPrizeDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList=activityUserDOList
                .stream()
                .map(activityUserDO -> {
                    ActivityDetailDTO.UserDTO userDTO=new ActivityDetailDTO.UserDTO();
                    userDTO.setUserName(activityUserDO.getUserName());
                    userDTO.setStatus(ActivityUserEnum.forName(activityUserDO.getStatus()));
                    userDTO.setUserId(activityUserDO.getUserId());
                    return userDTO;
                }).collect(Collectors.toList());

        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }





    private void checkActivity(CreateActivityParam createActivityParam) {

        if(createActivityParam==null){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        // 人员id在人员表中是否存在
        List<Long> userIds=createActivityParam.getActivityUserList()
                .stream()
                .map(ActivityUserListParam::getUserId)
                .distinct()
                .collect(Collectors.toList());

        List<Long> userIdsExist=userMapper.selectExistByIds(userIds);
        if(CollectionUtils.isEmpty(userIdsExist)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id->{
            if (!userIdsExist.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        // 奖品id在奖品表中是否存在

        List<Long> prizeIds=createActivityParam.getActivityPrizeList()
                .stream()
                .map(ActivityPrizeListParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        System.out.println(prizeIds);
        List<Long> prizeIdsExist=prizeMapper.selectExistByIds(prizeIds);
        if(CollectionUtils.isEmpty(prizeIdsExist)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id->{
            if(!prizeIdsExist.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });
        // 人员数量大于等于奖品数量
        int userAmount=createActivityParam.getActivityUserList().size();

        long prizeAmount=createActivityParam.getActivityPrizeList()
                .stream()
                .mapToLong(ActivityPrizeListParam::getPrizeAmount)
                .sum();

        if(prizeAmount>userAmount){
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        // 校验活动奖品等奖有效性

        createActivityParam.getActivityPrizeList().forEach(prize->{
            if (ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())==null) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });


        }


}
